---
sidebar_position: 1
title: Client
sidebar_label: Client
---

<div class="api-link">
  <div class="api-link-title">Client</div>
  <div class="api-link-sub-title">

[Read the API Reference »](/api/Hyper-Fetch/Class/Client.mdx)

  </div>
</div>

---

## Introduction

**`Client`** is a class that allows you to configure the server connection. It initializes the entire library’s
subsystems, such as `queues`, `caches`, and `interceptors`. It also allows you to create (based on its settings) the
requests necessary to execute requests. This way the data and information flow remains locked inside a given client; it
is isolated and does not affect other clients.

It was designed to be used as a singleton, where `Client` helps us create a global structure for making server requests
without duplicating logic in different parts of the application. In this approach, we can easily create a solid
structure and architecture for an application. This also facilitates test maintenance by dividing the necessary
configurations and types.

---

## Purpose

- Orchestrates the components and flow of the library
- Creates requests to provide global setup and environment
- Isolates clients from other clients and their events

---

## Initialization

```tsx
import { Client } from "@hyper-fetch/core";

export const client = new Client({ url: "http://localhost:3000" });
```

---

## Setting defaults

Because Hyper Fetch components are created inside Client, you can set global or default system values on it.

#### Request default setup

We can use the `setRequestDefaultOptions` method to specify the defaults for every created request.

---

## Features

### Authentication

To send authenticated requests, set up the `onAuth` interceptor. Set up the request with the `auth` option set to
**true**. Read more about authentication here.

[Read More](/guides/01-basic/authentication.mdx)

### Pre-Request Interceptor

Use the `onRequest` client method if you need to use the pre-request interceptor to modify the request before it’s sent.

### Post-Request Interceptors

There are several methods for intercepting a response from a request:

- `onError` which is triggered on request error response.
- `onSuccess` which is triggered on request success response.
- `onResponse` which is triggered on any response.

We can modify received data with this interceptor before it will be emitted to other sub-systems.

### Query Params

Client has a built-in query params encoding function; you can modify its options or provide your own function. Use the
`setQueryParamsConfig` method and the options listed below.

(@import HyperFetch QueryStringifyOptionsType type=returns)

To change the encoding function, use the `setStringifyQueryParams` method.

```tsx
client.setStringifyQueryParams((value: string) => encode(value));
```

### Header Mapper

By default, the header mapper behaves very simply: it checks if the content is FormData or JSON, and provides correct
headers to the request. You can create much more advanced setups with the `setHeaderMapper`. It allows you to define
custom logic that will be triggered before every request made in the client.

### Payload Mapper

The payload mapper’s default responsibility is to check if data is an instance of FormData or not. Based on this, you
can stringify non-FormData values or just pass the FormData to the request to be made. This allows file upload to be
supported out of the box.

---

## Typescript

Client has two generic types.

```tsx
class Client<GlobalErrorType, AdapterOptions>
```

- `GlobalErrorType` defines the error type used in all the requests. It should consist of an `Error` type and your
  default `ServerErrorType`. For some request’s individual error types, you can set up a `LocalErrorType` for each
  request.

- `AdapterOptions` is the generic responsible for shaping options passed to the adapter. Most likely you will change it
  only when you provide your custom adapter.

---

## Components

### [Cache](/api/Hyper-Fetch/Class/Cache.mdx)

Handles data storages and persistence. Can be adjusted with options when initializing `Client`.

---

### [Adapter](/api/Hyper-Fetch/Function/adapter.mdx)

Handles all requests within `Client`. Can be replaced with `setAdapter` method.

---

### [SubmitDispatcher](/api/Hyper-Fetch/Class/Dispatcher.mdx)

Handles the mutation requests and queueing. Can be adjusted with options when initializing the client.

---

### [FetchDispatcher](/api/Hyper-Fetch/Class/Dispatcher.mdx)

Handles the fetching requests and queueing. Can be adjusted with options when initializing the client.

---

### [AppManager](/api/Hyper-Fetch/Class/AppManager.mdx)

Handles the app focus and online state. Can be adjusted with options when initializing the client.

---

### [RequestManager](/api/Hyper-Fetch/Class/RequestManager.mdx)

Handles additional events and cancellation of requests. Can be adjusted with options when initializing the client.

---

### [LoggerManager](/api/Hyper-Fetch/Class/LoggerManager.mdx)

Handles the logging systems for debugging.

---

## Parameters

Configuration options

(@import HyperFetch ClientOptionsType type=returns)

<!-- ## Authentication

1. Adding the headers to the request during authentication

```tsx
export const client = new Client({ url }).onAuth((request) => {
  const state = store.getState();
  const authToken = state.auth.token;

  // Before each request with setting "auth: true" add the Bearer token
  return request.setHeaders({
    ...request.headers,
    Authorization: `Bearer ${authToken}`,
  });
});

export const getUsers = client.createRequest<{ name: string; email: string }>()({
  method: "GET",
  endpoint: "/users",
  auth: true, // Important!
});
```

2. Refreshing the token

```tsx
export const client = new Client({ url }).onError(async (res, request) => {
  const status = res[2];
  const refreshToken = localStorage.getItem(REFRESH_TOKEN_STORAGE_FIELD);

  // Check if request has the used value - this will ensure you will not go into infinite loop
  if (!request.used && refreshToken && status === 401) {
    // Prepare the refresh token request
    const postRefreshToken = client.createRequest<LoginResponse, LoginData>()({
      endpoint: "/refresh-token",
      method: "POST",
    });

    // Call the request to receive new tokens
    const [data] = await postRefreshToken.setData({ refreshToken }).exec();

    if (data) {
      // Safe the new tokens
      localStorage.setItem(TOKEN_STORAGE_FIELD, data.token);
      localStorage.setItem(REFRESH_TOKEN_STORAGE_FIELD, data.refreshToken);
      // Repeat the request
      return request.setUsed(true).send();
    }
  }
  // Return the initial response is something goes wrong
  return res;
});
``` -->

<!--

- connection graph with request and other systems

-->

<!--
## TODOS and examples

- TODO - subsection for Client -> providing own adapter (other than XHR) + options
- TODO - subsection for Client -> providing own cache.
- TODO - subsection for Client -> section for providing own Manager implementation.
- TODO - subsection for Client -> section for providing own queues implementation. -->
